/*
 * dom.js
 *
 * Copyright (C) 2006 - OS3 srl - http://www.os3.it
 *
 * Written by: Fabio Rotondo - fabio.rotondo@os3.it
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation;
 * version 2 of the License ONLY.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * NOTE: this is the GPL version of the library. If you want to include this 
 *       library inside a CLOSED SOURCE / PROPRIETARY software, you need 
 *       to purchase a COMMERCIAL LICENSE. See http://www.os3.it/license/
 *       for more info.
 */
 
/*
 * 	2009-09-07:	Added the $c() function is like a getAllElementsByClassName ( element, class_name, [ starting_container == document ] )
 */

/**
DOM Functions
=============

**/

liwe.dom = {};

// {{{ liwe.dom.get_offset_top ( elem )
/**
.. function:: liwe.dom.get_offset_top ( elem )

	Calculates top offset of ``elem`` in pixels starting from the beginning of the web page.

	:param elem: the DOM element

	:rtype: an integer containing the offset top

**/
liwe.dom.get_offset_top = function ( elm )
{
  	var o_top    = elm.offsetTop;
  	var o_parent = elm.offsetParent;

  	while ( o_parent && o_parent.tagName != "HTML" )
	{
		// if ( o_parent.style.position == 'absolute' ) return o_top;
    		o_top += o_parent.offsetTop;
    		o_parent = o_parent.offsetParent;
  	}
 
  	return o_top;
};
// }}}
// {{{ liwe.dom.get_offset_left ( elem )
/**
.. function:: liwe.dom.get_offset_left ( elem )

	Calculates left offset of ``elem`` in pixels starting from the beginning of the web page.

	:param elem: the DOM element

	:rtype: an integer containing the offset left
**/
liwe.dom.get_offset_left = function ( elm )  
{
	var o_left = elm.offsetLeft;
  	var o_parent = elm.offsetParent;

  	while ( o_parent )
	{
		// if ( o_parent.style.position == 'absolute' ) return o_left;
		// console.debug ( "parent: %o - o_parent: %s - left: %s", o_parent, o_parent.offsetParent, o_left );
		// if ( ! o_parent.offsetParent.offsetParent ) break;
    		o_left += o_parent.offsetLeft;
    		o_parent = o_parent.offsetParent;
  	}
 
  	return o_left;
};
// }}}
// {{{ liwe.dom.append_css ( css_file_name, id, as_first )
/**
.. function:: liwe.dom.append_css ( css_file_name, id, as_first )

	Appends a new CSS declaration in the web page. The CSS ``ccs_file_name`` will be added (and loaded by the browser)
	to the web page. Optionally, you can specify an ``id`` and you can also force the insertion as the first CSS in the
	page by setting ``as_first`` to true.

	:param css_file_name: the CSS file name you want to append
	:param id: the id of the new CSS declaration (optional)
	:param as_first: if set to ``true``, CSS will be added as the first CSS declaration of the page.

	:rtype: this function returns nothing
**/
liwe.dom.append_css = function ( css_file, id, as_first )
{
	var head = document.getElementsByTagName ( "head" ) [ 0 ];
	var new_css = document.createElement ( "link" );

	new_css.href = css_file;
	new_css.type = "text/css";
	new_css.rel  = "stylesheet";
	if ( id ) new_css.id = id;

	if ( as_first )
		head.insertBefore ( new_css, head.firstChild );
	else
		head.appendChild ( new_css );
};
// }}}
// {{{ liwe.dom.get_padding_width ( elem )
/**
.. function:: liwe.dom.get_padding_width ( elem )

	This function calculates the padding width of ``elem``

	:param elem: DOM element to calculate the padding width from.

	:rtype: an integer containing the padding width
**/
liwe.dom.get_padding_width = function ( el )
{
	var oldw = el.clientWidth;
	var neww;

	el.style.width = "0px";
	neww = el.clientWidth;

	el.style.width = ( oldw - neww ) + "px";

	return neww;
};
// }}}
// {{{ liwe.dom.get_padding_height ( elem )
/**
.. function:: liwe.dom.get_padding_height ( elem )

	This function calculates the padding height of ``elem``

	:param elem: DOM element to calculate the padding height from.

	:rtype: an integer containing the padding height
**/
liwe.dom.get_padding_height = function ( el )
{
	var oldh = el.clientHeight;
	var newh;

	el.style.height = "0px";
	newh = el.clientHeight;

	el.style.height = ( oldh - newh ) + "px";

	return newh;
};
// }}}
// {{{ liwe.dom.get_size ( elem )
/**
.. function:: liwe.dom.get_size ( elem )

	This function returns the width and height of ``elem``.

	:param elem: DOM element to calculate the size from.

	:rtype: a list with two integers: width and height respectively.
**/
liwe.dom.get_size = function ( el )
{
	el = $el ( el );
	if ( ! el ) return [ 0, 0 ];
	return [ el.clientWidth, el.clientHeight ];
};
// }}}
// {{{ liwe.dom.get_window_size ()
/**
.. function:: liwe.dom.get_window_size ()

	This function returns the width and height of the current window.

	:rtype: an object with two attributes ``width`` and ``height`` of the current window.
**/
liwe.dom.get_window_size = function ()
{
	var s = document.createElement ( "div" );
	s.style.position = "absolute";
	s.style.bottom = "0px";
	s.style.right  = "0px";
	s.style.width  = "1px";
	s.style.height  = "1px";
	s.style.visibility = "hidden";
	document.body.appendChild ( s );

	var w, h;
	w = liwe.dom.get_offset_left ( s ) + s.clientWidth;
	h = liwe.dom.get_offset_top ( s ) + s.clientHeight;

	document.body.removeChild ( s );

	return { "width": w, "height": h };
};
// }}}
// {{{ liwe.dom.create_element ( tag, name, parent )
/**
.. function:: liwe.dom.create_element ( tag, name, parent )

	This function creates a new DOM element and adds it to the document DOM.

	:param tag: the element tag you are creating.
	:param name: the name of the element you are creating.
	:param parent: the parent container of the new element (optional, defaults to document.body)

	:rtype: this function returns nothing.
**/
liwe.dom.create_element = function ( tag, name, parent )
{
	if ( ! parent ) parent = document.body;

	// if ( document.ActiveXObject ) tag = '<' + tag + ' name="' + name + '">';
	// if ( document.all ) tag = '<' + tag + ' name="' + name + '">';

	var e = document.createElement ( tag );
	e.style.position = 'absolute';
	e.style.top = "0px";
	e.style.right = "0px";
	e.id = name;
	e.name = name;
	parent.appendChild ( e );
	
	return e;
};
// }}}
// {{{ liwe.dom.remove_element ( elem, parent )
/**
.. function:: liwe.dom.remove_element ( elem, parent )

	This function removes the ``elem`` from the ``parent`` container.
	If ``parent`` has not been specified, it will be set to ``document.body``.

	:param elem: the DOM element you want to remove.
	:param parent: the element container of ``elem`` (optional, defaults to document.body).

	:rtype: this function returns nothing.
**/
liwe.dom.remove_element = function ( e, parent )
{
	if ( ! parent ) parent = document.body;

	parent.removeChild ( e );
};
// }}}
// {{{ liwe.dom.get_event_pos ( e )
/**
.. function:: liwe.dom.get_event_pos ( e )

	This function returns the ``x`` and ``y`` location where the event ``e``  has been fired.

	:param e: the event you want to get ``x`` and ``y``
**/
liwe.dom.get_event_pos = function ( e )
{
	var posx = 0, posy = 0;

	if ( e == null ) e = window.event;
	if ( e.pageX || e.pageY )
	{
		posx = e.pageX; 
		posy = e.pageY;
	} else if ( e.clientX || e.clientY ) {
	 	if ( document.documentElement.scrollTop )
		{
	 		posx = e.clientX + document.documentElement.scrollLeft;
	 		posy = e.clientY + document.documentElement.scrollTop;
	 	} else {
	 		posx = e.clientX + document.body.scrollLeft;
	 		posy = e.clientY + document.body.scrollTop;
	 	}
	 }

	return [ posx, posy ];
};
// }}}
// {{{ liwe.dom.has_class ( elem, class_name )
/**
.. function:: liwe.dom.has_class ( elem, class_name )

	This function returns ``true`` if ``elem`` has ``class_name``.

	:param elem: the element to check class name.

	:param class_name: the name of the class.

	:rtype: ``true`` if ``elem`` has ``class_name``, ``false`` otherwise.
**/
liwe.dom.has_class = function ( el, class_name )
{
	if ( ! el || ! el.className ) return false;

	var names = {};
	var cname = list ( el.className.split ( " " ) );

	cname.iterate ( function ( v ) { names [ v ] = 1; } );

	return class_name in names;
};
// }}}
// {{{ liwe.dom.add_class ( elem, class_name )
/**
.. function:: liwe.dom.add_class ( elem, class_name )

	This function adds ``class_name`` to ``elem``.

	:param elem: the target element.

	:param class_name: the new class name to be added to ``elem``.
**/
liwe.dom.add_class = function ( target, class_name )
{
	if ( ! target ) return;
	target = $el ( target );
	if ( ! target || typeof ( target ) != 'object' ) return;

	if ( ! target.className ) 
		target.className = class_name;

	else
	{
		var res = target.className;

		list ( class_name.split ( " " ) ).iterate ( function ( v ) {
			if ( ! liwe.dom.has_class ( target, v ) )
				res += " " + v;
		} );

		target.className = res.strip ();
	}
};
// }}}
// {{{ liwe.dom.del_class ( elem, class_name )
/**
.. function:: liwe.dom.del_class ( elem, class_name )

	This function removes ``class_name`` to ``elem``.

	:param elem: the target element.

	:param class_name: the class name to be removed from ``elem``.
**/
liwe.dom.del_class = function ( target, class_name )
{
	if ( ! target ) return;
	target = $el ( target );
	if ( ! target || typeof ( target ) != 'object' ) return;

	if ( ! target.className || target.className == '' )
		return;

	var names = {};
	var tmp = list ( class_name.split ( " " ) );
	var cname = target.className.split ( " " );
	var t, l = cname.length;
	var s = '', v;

	tmp.iterate ( function ( v ) { names [ v ] = 1; } );
	
	for ( t = 0; t < l; t ++ )
	{
		v = cname [ t ].strip ();
		if ( ! v ) continue;
		if ( v in names ) continue;

		s += cname [ t ] + " ";
	}
	
	target.className = s.strip ();
};
// }}}

liwe.dom.reload_css = function ()
{
	var i, a, s;
	a = document.getElementsByTagName ( 'link' );
	for( i = 0; i < a.length; i++ )
	{
		s = a [ i ];
		if ( s.rel.toLowerCase ().indexOf ( 'stylesheet' ) >= 0 && s.href )
		{
			var h = s.href.replace ( /(&|%5C?)forceReload=\d+/, '' );
			s.href = h + ( h.indexOf ( '?' ) >=0 ? '&' : '?' ) + 'forceReload=' + ( new Date ().valueOf () );
		}
	}
};

liwe.dom.get_scroll = function ()
{
	if ( typeof pageYOffset != 'undefined' )
		return [ pageXOffset, pageYOffset ];
	else
	{
		var b = document.body;
		var d = document.documentElement;
		d = d.clientHeight ? d : b;
		return [ d.scrollLeft, d.scrollTop ];
	}
};

/*
Object.prototype.hide = function ()
{
	console.warn ( 'Object.hide() is deprecated! Use liwe.dom.hide()' );
	console.trace ();
	if ( ! this.style || this.style.display == 'none' ) return;

	this._old_display = this.style.display;
	this.style.display = 'none';
};

Object.prototype.show = function ()
{
	console.warn ( 'Object.show() is deprecated! Use liwe.dom.show()' );
	console.trace ();
	if ( ! this.style ) return;

	if ( this._old_display )
		this.style.display = this._old_display;
	else
		this.style.display = 'block';

	this._old_display = null;
};
*/

liwe.dom.show = function ( el )
{
	el = $el ( el );
	if ( ! el || ! el.style ) return;
	
	if ( el._old_display )
		el.style.display = el._old_display;
	else
		el.style.display = 'block';
};

liwe.dom.hide = function ( el )
{
	el = $el ( el );
	if ( ! el || ! el.style || el.style.display == 'none' ) return;

	el._old_display = el.style.display;
	el.style.display = 'none';
};

liwe.dom.parse = function ( str )
{
	var d = document.createElement ( "div" );
	var res = [];
	var el;

	d.innerHTML = str;

	l = d.childNodes.length;

	while ( d.childNodes.length )
	{
		el = d.childNodes [ 0 ];

		res.push ( el );
		d.removeChild ( el );
	}

	return res;
};

liwe.dom.append_children = function ( el, children )
{
	if ( typeof ( children ) == 'string' ) children = liwe.dom.parse ( children );

	var t, l = children.length;

	el = $el ( el );

	for ( t = 0; t < l; t ++ )
		el.appendChild ( children [ t ] );
};

// {{{ liwe.dom.get_style ( elem, style )
liwe.dom.get_style = function ( elem, style )
{
	var computed_style = null;
	if ( typeof ( elem.currentStyle ) != 'undefined' )
		computed_style = elem.currentStyle;
	else
		computed_style = document.defaultView.getComputedStyle ( elem, null );
	if ( ! style )
		return computed_style;
	else
		return computed_style [ style ];
};
// }}}
// {{{ liwe.dom.is_child_of ( parent, child )
liwe.dom.is_child_of = function ( parent, child )
{
	if( child != null )
	{			
		while( child.parentNode )
		{
			if( ( child = child.parentNode ) == parent )
			{
				return true;
			}
		}
	}
	return false;
};
// }}}
// {{{ liwe.dom.supports = function ( prop )
liwe.dom.supports = function ( prop )
{
	console.warn ( "liwe.dom.supports() is DEPRECATED. Use liwe.utils.supports() instead." );
	console.trace ();
	return liwe.utils.supports ( prop );
};
// }}}
// {{{ liwe.dom.tableize = function ()
liwe.dom.tableize = function ()
{
	if ( ( ! liwe.browser.ie ) && ( ! liwe.browser.version < 8 ) ) return;
	
	function _replace ( table_start, table )
	{
		var parent = table_start.parentNode;
		var next_sibling = table_start.nextSibling;
		parent.removeChild ( table_start );
		parent.insertBefore ( table, next_sibling );
	}

	function _add_cell ( row, div )
	{
		var c;
		cell = document.createElement ( "td" );
		cell.setAttribute ( "vAlign", "top" );
		cell.className = div.className;

		c = cell.appendChild ( div.firstChild.cloneNode ( true ) );
		c.style.display = "block";

		row.appendChild ( cell );
	}

	var divs = document.getElementsByTagName ( "div" );
	var t, l = divs.length, div;
	var table = null, tbody = null;
	var row = null, cell = null;
	var table_start = null;

	for ( t = 0; t < l; t ++ )
	{
		div = divs [ t ];
		if ( ! div.className ) continue;

		if ( div.className.indexOf ( 'table' ) != -1 )
		{
			if ( table_start )
			{
				_replace ( table_start, table );
				table = null;
			}

			table_start = div;
			table = document.createElement ( "table" );
			tbody = document.createElement ( "tbody" );
			table.appendChild ( tbody );
			table.className = div.className;
			table.style.border = "1px dotted black";
		} else if ( div.className.indexOf ( "cell-first" ) != -1 ) {
			row = document.createElement ( "tr" );
			tbody.appendChild ( row );
			_add_cell ( row, div );
		} else if ( div.className.indexOf ( "cell" ) != -1 ) {
			_add_cell ( row, div );
		}
	}

	if ( table )
		_replace ( table_start, table );
};
// }}}
// {{{ function $c ( element, class_name, base )
/*
 * $c ( element, class_name, base )
 *
 * element - Element kind (eg. "div" )
 * class_name - Name of class that the element should contain
 * base - Starting point for getElementsByTagName() (default: document)
 *
 * returns: a list of elements of kind ``element`` with the ``class_name`` set
 */
function $c ( element, class_name, base, skip_class_name )
{
	if ( ! base ) base = document;
	if ( ! skip_class_name ) skip_class_name = "0xdeadbeef";

	base = $el ( base );
	var elements = base.getElementsByTagName ( element );
	var t, l = elements.length;
	var cls= [], res = [], el;

	for ( t = 0; t < l; t ++ )
	{
		el = elements [ t ];
		cls = el.className.split ( ' ' );
		try
		{
			if ( cls.indexOf ( class_name ) == -1 ) continue;
			if ( cls.indexOf ( skip_class_name ) != -1 ) continue;
		} catch ( err )
		{	
			if ( el.className.indexOf ( class_name ) == -1 ) continue;
			if ( el.className.indexOf ( skip_class_name ) != -1 ) continue;
		}

		res.push ( el );
	}

	return res;
}
// }}}
// {{{ function $d ( el, mode )
/*
 * $d ( el, mode )
 *
 * Sets the style.display of element ``el`` to ``mode``
 */
function $d ( el, mode )
{
	var e = $( el );
	if ( ! e ) 
	{
		console.warn ( "Element: %s not found", el );
		console.trace ();
		return;
	}

	e.style.display = mode;
}
// }}}
// {{{ function get_highest_zindex ( check_on )
/*
 * get_highest_zindex ( check_on ) 
 *
 * Compare and return highest z-index among dom elements. Param 'check_on' define on which dom element compare to ( default is 'div' ) 
 */
liwe.dom.get_highest_zindex = function ( check_on )
{
        var el = ( ! check_on ) ? 'div' : check_on;
        var z = 0, tmp, val;
        var elems = document.getElementsByTagName ( el );
        for ( var c = 0; c < elems.length; c++ )
	{
		//if ( document.defaultView ) val = document.defaultView.getComputedStyle ( elems[c], null ).getPropertyValue ( 'z-index' );
		val = liwe.dom.get_style ( elems [ c ], 'zIndex' );
		//if ( elems [ c ].currentStyle ) val = elems [ c ].currentStyle [ 'zIndex' ];
		//if ( window.getComputedStyle ) val = window.getComputedStyle ( elems[c], null ).getPropertyValue ( 'z-index' );

		tmp = parseInt ( val, 10 );
	
		if ( ( ! isNaN ( tmp ) ) && ( tmp  > z ) ) z = tmp;
	} 
        return z;
};
// }}}
// {{{ function is_visible ( elem )
/*
 * function is_visible ( elem )
 *
 * traverse dom up to first hidden parentNode if any. 
 */
liwe.dom.is_visible = function ( el, top_elem )
{
	//FIXME:error if top_elem is not defined 
	var last = document.body;
	var dis, vis;

	el = $el ( el );

	if ( ! el ) return false;
	if ( top_elem ) last = $el ( top_elem );

	while ( 1 )
	{
		vis = liwe.dom.get_style ( el , 'visibility' );
		dis = liwe.dom.get_style ( el , 'display' );
		
		if ( vis == 'hidden' || dis == 'none' ) return false;
		if ( el == last ) return true;

		el = el.parentNode;
		if ( ! el ) return true;
	}
};
// }}}
// {{{ function get_body_size ()
liwe.dom.get_body_size = function ()
{
	var w = document.documentElement ? document.documentElement.clientWidth : document.body.clientWidth;
	var h = document.documentElement ? document.documentElement.clientHeight : document.body.clientHeight;
	return [ w, h ];
};
// }}}
